package com.zan.zanchat.common.user.service.impl;

import com.zan.zanchat.common.common.annotation.RedissonLock;
import com.zan.zanchat.common.common.event.UserBlackEvent;
import com.zan.zanchat.common.common.event.UserRegisterEvent;
import com.zan.zanchat.common.common.utils.AssertUtil;
import com.zan.zanchat.common.user.dao.BlackDao;
import com.zan.zanchat.common.user.dao.ItemConfigDao;
import com.zan.zanchat.common.user.dao.UserBackpackDao;
import com.zan.zanchat.common.user.dao.UserDao;
import com.zan.zanchat.common.user.domain.entity.*;
import com.zan.zanchat.common.user.domain.enums.BlackTypeEnum;
import com.zan.zanchat.common.user.domain.enums.ItemEnum;
import com.zan.zanchat.common.user.domain.enums.ItemTypeEnum;
import com.zan.zanchat.common.user.domain.vo.req.BlackReq;
import com.zan.zanchat.common.user.domain.vo.resp.BadgeResp;
import com.zan.zanchat.common.user.domain.vo.resp.UserInfoResp;
import com.zan.zanchat.common.user.service.UserService;
import com.zan.zanchat.common.user.service.adapter.UserAdapter;
import com.zan.zanchat.common.user.service.cache.ItemCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author Zan
 * @Create 2024/3/8 16:56
 * @Description :
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    @Resource
    private UserBackpackDao userBackpackDao;

    @Resource
    private ItemCache itemCache;

    @Resource
    private ItemConfigDao itemConfigDao;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private BlackDao blackDao;

    @Override
    @Transactional
    public Long register(User insertUser) {
        userDao.save(insertUser);
        // todo 用户注册的事件 (可以使用MQ【微服务】，也可以使用Spring的事件)
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, insertUser));
        return insertUser.getId();
    }

    @Override
    public UserInfoResp getUserInfo(Long uid) {
        User user = userDao.getById(uid);
        Integer modifyNameCount = userBackpackDao.getCountByValidItemId(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        return UserAdapter.buildUserInfo(user, modifyNameCount);
    }

    /**
     * 名字最长6位，且唯一，超过6位自动转换
     *
     * @param uid
     * @param name
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 为了保证事务对所有异常生效
    @RedissonLock(key = "#uid")
    public void modifyName(Long uid, String name) {
        // 判断是否重复
        User oldUser = userDao.getByName(name);
        AssertUtil.isEmpty(oldUser, "名字已经被抢占了，请换一个名字");
        // 获取最后一张老的改名卡
        UserBackpack modifyNameItem = userBackpackDao.getFirstValidItem(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        AssertUtil.isNotEmpty(modifyNameItem, "改名卡不够咯，等后续的活动送改名卡吧");
        // 使用改名卡
        boolean success = userBackpackDao.useItem(modifyNameItem);
        if (success) {
            // 使用成功后进行改名
            userDao.modifyName(uid, name);
        }
    }

    @Override
    public List<BadgeResp> badges(Long uid) {
        // 1.获取所有的徽章列表 - 缓存redis和本地，经常改用放在redis，不常变化就存在本地
        List<ItemConfig> itemConfigs = itemCache.getByType(ItemTypeEnum.BADGE.getType());
        // 2.查询判断用户有哪些徽章 - uid和对应的徽章itemId集合
        List<UserBackpack> backpacks = userBackpackDao.getByItemIds(uid, itemConfigs.stream().map(ItemConfig::getId).collect(Collectors.toList()));
        // 查询用户当前佩戴的徽章itemId
        User user = userDao.getById(uid);
        return UserAdapter.buildBadgeResp(itemConfigs, backpacks, user);
    }

    @Override
    public void wearingBadge(Long uid, Long itemId) {
        // 1.确保有物品
        UserBackpack firstValidItem = userBackpackDao.getFirstValidItem(uid, itemId);
        AssertUtil.isNotEmpty(firstValidItem, "您还没有找个徽章，快去获得吧");
        // 2.确保这个物品是徽章
        ItemConfig itemConfig = itemConfigDao.getById(firstValidItem.getItemId());
        AssertUtil.equal(itemConfig.getType(), ItemTypeEnum.BADGE.getType(), "只有徽章才能佩戴");
        // 3.佩戴徽章 - 修改
        userDao.wearingBadge(uid, itemId);
    }

    /**
     * 拉黑用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void black(BlackReq req) {
        Long uid = req.getUid();
        Black userBlack = new Black();
        userBlack.setType(BlackTypeEnum.UID.getType());
        userBlack.setTarget(uid.toString());
        blackDao.save(userBlack);
        // 获取user的ip，进行拉黑 （多次拉黑，拉黑uid，还要拉黑ip）
        User user = userDao.getById(uid);
        blackIp(Optional.ofNullable(user.getIpInfo()).map(IpInfo::getCreateIp).orElse(null));
        blackIp(Optional.ofNullable(user.getIpInfo()).map(IpInfo::getUpdateIp).orElse(null));
        applicationEventPublisher.publishEvent(new UserBlackEvent(this, user));
    }

    private void blackIp(String ip) {
        if (StringUtils.isBlank(ip)) {
            return;
        }
        try {
            Black userIpBlack = new Black();
            userIpBlack.setType(BlackTypeEnum.IP.getType());
            userIpBlack.setTarget(ip);
            blackDao.save(userIpBlack);
        } catch (Exception e) {
            log.error("blackIp:{}", ip);
        }
    }
}
